package cn.y.operation.framework.shiro;

import cn.y.operation.application.core.entity.SysUser;
import cn.y.operation.framework.constants.Constants;
import cn.y.operation.framework.utils.dev.BrowserUtils;
import cn.y.operation.framework.utils.dev.DateUtils;
import cn.y.operation.framework.utils.dev.StringUtils;
import cn.y.operation.framework.utils.system.ResponseUtils;
import cn.y.operation.framework.utils.system.UserUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName: MyFormAuthenticationFilter
 * @author: 叶子豪
 * @create: 2021-03-15 10:25
 */
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
    private final Logger logger = LoggerFactory.getLogger(MyFormAuthenticationFilter.class);

    private final Cache<String, AtomicInteger> passwordRetryCache;

    public MyFormAuthenticationFilter(CacheManager cacheManager) {
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }

    /*
     * 重写createToken()方法里的获取IP的方法
     */
    @Override
    protected String getHost(ServletRequest request) {
        HttpServletRequest req = (HttpServletRequest) request;
        return StringUtils.getRemoteAddr(req);
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = createToken(request, response);
        if (token == null) {
            String msg = "create AuthenticationToken error";
            throw new IllegalStateException(msg);
        }
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        String username = (String) token.getPrincipal();
        boolean adminLogin = false;
        // 验证码校验
        // if(isDisabled(username)){
        // return onLoginFailure(token,adminLogin,new
        // DisabledException(),request, response);
        // }
        try {
            Subject subject = getSubject(request, response);
            subject.login(token);
            return onLoginSuccess(token, adminLogin, subject, request, response);
        } catch (AuthenticationException e) {
            logger.info("", e);
            return onLoginFailure(token, adminLogin, e, request, response);
        }
    }

    /**
     * 登录成功
     */
    private boolean onLoginSuccess(AuthenticationToken token, boolean adminLogin, Subject subject,
                                   ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        SysUser user = (SysUser) subject.getPrincipal();
        // 登陆成功后的一些日志操作
        // CmsUser user = cmsUserMng.findByUsername(username);
        // String ip = RequestUtils.getIpAddr(req);
        // userMng.updateLoginInfo(user.getId(), ip);
        // //管理登录
        // if(adminLogin){
        // cmsLogMng.loginSuccess(req, user);
        // }
        // 清除需要验证码cookie
        // passwordRetryCache.remove(user.getName());

        // 为druid的session监控 添加当前登录用户的用户名
        String name = UserUtils.getShiroUser().getLoginName();
        req.getSession().setAttribute(Constants.LOGIN_USERNAME, name);
        logger.error("LOG_LOGIN-Success " + name + " ,登陆IP--> " + UserUtils.getShiroUser().getLoginIp()
                + ",浏览器信息是--> " + BrowserUtils.checkBrowse(req) + " UserAgent--> " + req.getHeader("User-Agent"));
        Map<String, Object> map = new HashMap<>();
        map.put("userId", user.getUserId());
        map.put("loginIp", UserUtils.getShiroUser().getLoginIp());
        map.put("loginDate", DateUtils.getDateToString());
        //userPermissionService.updateUserByUid(map);//保存登录记录

        passwordRetryCache.remove(req.getSession().getId());
        String lang = StringUtils.isNotEmpty(req.getParameter("lang")) ? req.getParameter("lang") : "zh";
        Locale locale = new Locale("zh", "CN");
        if (StringUtils.isNotEmpty(lang) && lang.startsWith("en")) {
            locale = new Locale("en", "US");
        }
        req.getSession().setAttribute(SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale);
        req.getSession().setAttribute("lang", lang);
        return super.onLoginSuccess(token, subject, request, response);
    }

    /**
     * 登录失败
     */
    private boolean onLoginFailure(AuthenticationToken token, boolean adminLogin, AuthenticationException e,
                                   ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        AtomicInteger retryCount = passwordRetryCache.get(req.getSession().getId());
        if (retryCount == null) {
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(req.getSession().getId(), retryCount);
        }
        retryCount.incrementAndGet();
        // 管理登录
        // if(adminLogin){
        // cmsLogMng.loginFailure(req,"username=" + username);
        // }
        // 如果是Ajax请求
        if (req.getHeader("accept").contains("application/json") || (req.getHeader("X-Requested-With") != null
                && req.getHeader("X-Requested-With").contains("XMLHttpRequest"))) {
            String exceptionName = e.getClass().getSimpleName();
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put("success", false);
            resultMap.put("isCaptchaRequired", false);
            /*if (retryCount.get() > Constants.LOGIN_TRY_TIME) {//多次失败开启校验码
                resultMap.put("isCaptchaRequired", true);
            }*/
            switch (exceptionName) {
                case "UnknownAccountException":
                    resultMap.put("errcode", "account.error");
                    resultMap.put("message", "您输入的账户名和密码不匹配，请重新输入");
                    resultMap.put("type", "username");
                    break;
                case "IncorrectCredentialsException":
                    resultMap.put("errcode", "account.password.error");
                    resultMap.put("message", "您输入的账户名和密码不匹配，请重新输入");
                    resultMap.put("type", "password");
                    break;
                case "CaptchaErrorException":
                    resultMap.put("errcode", "verification.code.error");
                    resultMap.put("message", "验证码错误");
                    resultMap.put("type", "captcha");
                    break;
                case "CaptchaRequiredException":
                    resultMap.put("errcode", "verification.code.null");
                    resultMap.put("message", "验证码不能为空");
                    resultMap.put("type", "captcha");
                    break;
                case "LockedAccountException":
                    resultMap.put("errcode", "account.lock");
                    resultMap.put("message", "账号被锁定或未激活");
                    resultMap.put("type", "system");
                    break;
                case "ExcessiveAttemptsException":
                    resultMap.put("errcode", "login.error.num");
                    resultMap.put("message", "错误次数超过限制");
                    resultMap.put("type", "system");
                    break;
                default:
                    resultMap.put("errcode", "login.error");
                    resultMap.put("message", "登录失败，请联系管理员");
                    resultMap.put("type", "system");
                    break;
            }
            ResponseUtils.renderJson(res, resultMap);
        }
        setFailureAttribute(request, e);
        UsernamePasswordToken utoken = (UsernamePasswordToken) token;
        logger.error("LOG_LOGIN-fail--> " + utoken.getUsername() + e.getClass().getSimpleName()
                + ",登陆的IP是 -->" + utoken.getHost()
                + ",浏览器信息是--> " + BrowserUtils.checkBrowse(req)
                + " UserAgent--> " + req.getHeader("User-Agent"));
        return false;
    }
}
